; (function (global, $, window, undefined) {

    // Lets run in ECMAScript 5 Strict Mode to
    // weed out unsafe operations and potential bugs
    "use strict";
;/* =============================================================
* codagenicJS Library
* http://www.codagenic.com/
* =============================================================
* Copyright 2012 Codagenic, All Rights Reserved.
*
* Created by Byron Salau
* ============================================================ */



	// Our library       
	if ('undefined' === typeof Codagenic)
	{
		var Codagenic = {};

		if ('undefined' !== typeof window)
		{
			window.Cg = window.Codagenic = Codagenic;
		}
	}

	//Extend jQuery
	if ('undefined' === typeof $.codagenic)
	{
		$.codagenic = {};
	}

	// some references
	var undef,
		DOC = window.document,
		NAV = window.navigator.userAgent.toLowerCase(),
		HASH = window.location.hash.replace(/#\//, ''),
		$DOC   = $( DOC ),
		$WIN = $(window),
		ISTOUCH = !!('ontouchstart' in window),
		ISIFRAME = (window.location !== window.parent.location) ? true : false,

		// constants
		VERSION = "@VERSION",
		DEBUG = true,
		F = function(){},
		NULL_TYPE = 'Null',
		UNDEFINED_TYPE = 'Undefined',
		BOOLEAN_TYPE = 'Boolean',
		NUMBER_TYPE = 'Number',
		STRING_TYPE = 'String',
		OBJECT_TYPE = 'Object',
		ERROR_TYPE = 'Error',
		FUNCTION_TYPE = 'Function',
		FUNCTION_CLASS = '[object Function]',
		BOOLEAN_CLASS = '[object Boolean]',
		NUMBER_CLASS = '[object Number]',
		STRING_CLASS = '[object String]',
		ARRAY_CLASS = '[object Array]',
		DATE_CLASS = '[object Date]',
		CODAGENIC_O_CLASS = '[object Codagenic.O]',
		NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
			typeof JSON.stringify === 'function' &&
			JSON.stringify(0) === '0',

		// Some versions of JScript fail to enumerate over properties, names of which 
		// correspond to non-enumerable properties in the prototype chain
		DONT_ENUMS = ['toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor'],
		IS_DONTENUM_BUGGY = (function ()
		{
			for (var p in { toString: 1 })
			{
				// check actual property name, so that it works with augmented prototype
				if (p === 'toString') {return false;}
			}
			return true;
		})();

	// Version refs
	Codagenic.version = $.codagenic.version = VERSION;

	// Application Namespaces
	Codagenic.Models = {};
	Codagenic.Views = {};
	Codagenic.Controllers = {};


	/*
	*  Cross-broswer add properties
	*
	*/
	Codagenic.addProperty = function (obj, name, settings)
	{

		// wrapper functions
		var
			oldValue = obj[name],
			getFn = function ()
			{
				return settings.get.apply(obj, [oldValue]);
			},
			setFn = function (newValue)
			{
				oldValue = settings.set.apply(obj, [newValue]);
				return oldValue;
			};

		try
		{
			// Modern browsers, IE9+, and IE8 (must be a DOM object),
			Object.defineProperty(obj, name, {
				get: getFn,
				set: setFn
			});

			return;
		}
		catch (e)
		{
		}

		try
		{
			// Older Mozilla
			obj.__defineGetter__(name, getFn);
			obj.__defineSetter__(name, setFn);

			return;
		}
		catch (e)
		{
		}


		// IE6-7
		// must be a real DOM object (to have attachEvent) and must be attached to document (for onpropertychange to fire)
		var onPropertyChange = function (e)
		{

			if (event.propertyName === name)
			{
				// temporarily remove the event so it doesn't fire again and create a loop
				obj.detachEvent("onpropertychange", onPropertyChange);

				// get the changed value, run it through the set function
				var newValue = setFn(obj[name]);

				// restore the get function
				obj[name] = getFn;
				obj[name].toString = getFn;

				// restore the event
				obj.attachEvent("onpropertychange", onPropertyChange);
			}
		};

		obj[name] = getFn;
		obj[name].toString = getFn;

		obj.attachEvent("onpropertychange", onPropertyChange);

	};
;/* =============================================================
 * codagenicJS Library
 * http://www.codagenic.com/
 * =============================================================
 * Copyright 2012 Codagenic, All Rights Reserved.
 *
 * Created by Byron Salau
 * ============================================================ */


/**
*   Get Objects type  
*
*   @param   object   obj         the object to test
*   @returns string               returns the objects type
*/
Codagenic.type = function (obj)
{

    if (obj === null)
        {return NULL_TYPE;}

    switch (typeof obj)
    {
        case 'boolean': return BOOLEAN_TYPE;
        case 'number': return NUMBER_TYPE;
        case 'string': return STRING_TYPE;
        case 'undefined': return UNDEFINED_TYPE;
        case 'error': return ERROR_TYPE;
        case 'function': return FUNCTION_TYPE;
        default: return OBJECT_TYPE;
    }
};

/**
*   Test if jQuery   
*
*   @param   object   obj         the object to test
*   @returns bool                 returns 'true' if object is instance of jQuery
*/
Codagenic.isJquery = function (obj)
{
    return obj instanceof $;
};

/**
*   Test if null
*
*   @param   object   obj         the object to test
*   @returns bool                 returns 'true' if object is null
*/
Codagenic.isNull = function (obj)
{
    return obj === null;
};

/**
*   Test if DOM Element
*
*   @param   object   obj         the object to test
*   @returns bool                 returns 'true' if object is an element
*/
Codagenic.isElement = function (obj)
{
    return !!(obj && obj.nodeType === 1);
};

/**
*   Test if string
*
*   @param   object   obj         the object to test
*   @returns bool                 returns 'true' if object is a string
*/
Codagenic.isString = function (obj)
{
    return Codagenic.type(obj) === STRING_TYPE;
};

/**
*   Test if object
*
*   @param   object   obj         the object to test
*   @returns bool                 returns 'true' if object is of object type
*/
Codagenic.isObject = function (obj)
{
    return obj !== null && Codagenic.type(obj) === OBJECT_TYPE;
};

/**
*   Test if Array
*
*   @param   object   obj         the object to test
*   @returns bool                 returns 'true' if object is Array
*/
Codagenic.isArray = function (obj)
{
    return obj !== null && Codagenic.type(obj) === OBJECT_TYPE && 'splice' in obj && 'join' in obj;
};

/**
*   Test if function
*
*   @param   object   obj         the object to test
*   @returns bool                 returns 'true' if object is a function
*/
Codagenic.isFunction = function (obj)
{
    return (Object.prototype.toString.call(obj).indexOf(FUNCTION_TYPE) > -1);
};


/**
*   Test if number
*
*   @param   object   obj         the object to test
*   @returns bool                 returns 'true' if object is a number
*/
Codagenic.isNumber = function (obj)
{
	return !isNaN(parseFloat(obj)) && isFinite(obj);
};


/**
*   Test if boolean
*
*   @param   object   obj         the object to test
*   @returns bool                 returns 'true' if object is a boolean
*/
Codagenic.isBoolean = function (obj)
{
    return Codagenic.type(obj) === BOOLEAN_TYPE;
};

/**
*   Test if date
*
*   @param   object   obj         the object to test
*   @returns bool                 returns 'true' if object is a date
*/
Codagenic.isDate = function (obj)
{
    return obj instanceof Date;
};


/**
*   Test if defined
*
*   @param   object   obj         the object to test
*   @returns bool                 returns 'true' if object is defined
*/
Codagenic.isDefined = function (obj)
{
    return Codagenic.type(obj) !== UNDEFINED_TYPE;
};

/**
*   Test if error
*
*   @param   object   obj         the object to test
*   @returns bool                 returns 'true' if object is error object
*/
Codagenic.isError = function (obj)
{
    return Codagenic.type(obj) === ERROR_TYPE;
};


Codagenic.isSet = function (obj)
{
	/// <summary>Tests if object set</summary>
	/// <param name="obj" type="Object">The object to test</param>
	/// <returns type="bool">true if the object is determined to be set and not empty</returns>

    var o, self = this;

    if (!self.isDefined(obj))
        {return false;}

    if (self.isString(obj))
        {return obj !== '';}

    if (self.isArray(obj))
        {return obj.length > 0;}

    if (self.isNumber(obj))
        {return obj > 0;}

    if (self.isDate(obj) || self.isFunction(obj))
        {return true;}

    if (self.isError(obj))
        {return obj.message.length > 0;}

    if (self.isObject(obj))
    {
        for (var prop in obj)
        {
            if (obj.hasOwnProperty(prop))
                {return true;}
        }

        return false;
    }

    throw "Unable to determine if '" + obj + "' set or empty";
};


Codagenic.isEqual = function (a, b, sortArrays)
{
	/// <summary>
	/// Test isEqual   
	/// Determines whether two objects are "identical" or not, meaning they have the same (===) values and/or property values.
	/// In order for to return true with no sortArrays set to true, a and b arguments must both be one of three three following:
	/// a) a `string`, `boolean`, or 'number` type, and `a === b`
	/// b) an `array` object, and have the same element values in the same order
	/// c) an `object` type, and a), b), and./or c) are true for all its properties values, irrespective of property name order
	/// </summary>
	/// <param name="a" type="Object">An Object</param>
	/// <param name="b" type="Object">An Object to test</param>
	/// <returns type="bool">returns 'True' if the objects are determined to be the same</returns>


	function sort(object)
	{
		if (sortArrays === true && Array.isArray(object))
		{
			return object.sort();
		}
		else if (typeof object !== "object" || object === null)
		{
			return object;
		}

		return Object.keys(object).sort().map(function (key)
		{
			return {
				key: key,
				value: sort(object[key])
			};
		});
	}

	return JSON.stringify(sort(a)) === JSON.stringify(sort(b));
};

/**
*   Deep copy object
*
*   @param   object   obj         the object to copy
*   @returns object               returns a deep copy of the orginal object
*/
Codagenic.copy = function (obj)
{
    var out, i;

    if (Object.prototype.toString.call(obj) === ARRAY_CLASS)
    {
        var len = obj.length; out = []; i = 0;
        for (; i < len; i++)
        {
            out[i] = Codagenic.copy(obj[i]);
        }
        return out;
    }
    if (typeof obj === 'object')
    {
        out = {};
        for (i in obj)
        {
            out[i] = Codagenic.copy(obj[i]);
        }
        return out;
    }

    return obj;
};

/**
*   Unique ID
*
*   @returns string		
*/
Codagenic.UniqueId = function ()
{
	// always start with a letter (for DOM friendlyness)
	var idstr = String.fromCharCode(Math.floor((Math.random() * 25) + 65));
	do
	{
		// between numbers and characters (48 is 0 and 90 is Z (42-48 = 90)
		var ascicode = Math.floor((Math.random() * 42) + 48);
		if (ascicode < 58 || ascicode > 64)
		{
			// exclude all chars between : (58) and @ (64)
			idstr += String.fromCharCode(ascicode);
		}
	} while (idstr.length < 32);

	return ('CG_' + idstr);
};

/**
*   Callback
*
*   @returns bool		
*/
Codagenic.Callback = function(func, obj)
{
    if ($.isFunction(func))
    {
        func(obj);
        return true;
    }

    return false;
}
;/* =============================================================
* codagenicJS Library
* http://www.codagenic.com/
* =============================================================
* Copyright 2012 Codagenic, All Rights Reserved.
*
* Created by Byron Salau
* ============================================================ */



/**
*  Creates an Codagenic.Array from an Array like object.
*
*  @param   Array     arr     The native array object 
*
*  @returns Codagenic.Array
*/
Codagenic.A = function (arr)
{
    if (this instanceof Codagenic.A)
    {
        if (Codagenic.isArray(arr))
        {
            for (var i = 0; i < arr.length; i++)
            {
                this.push(arr[i]);
            }
        }
        else if (Codagenic.isSet(arr))
        {
            this.push(arr);
        }

        return this;
    }

    return new Codagenic.A(arr);

};

/*
*    Extend Native Array
*/
Codagenic.A.prototype = [];


/*
*   Re-order Array containting numbers
* 
*   @param   string     dir     Expects 'ASEC' for ascending (default) or 'DESC' for decending
*/
Codagenic.A.prototype.numSort = function (dir)
{
    if(Codagenic.isString(dir))
    {
        if(dir.toUpperCase() !== 'ASEC' && dir.toUpperCase() !== 'DESC')
            {return undefined;}
    }
    if (dir === 'DESC')
    {
        this.sort(function (a, b) { return a + b; });
    }
    else
    {
        this.sort(function (a, b) { return a - b; });
    }
    return this;
};


/*
*  Itterates over the array items
*
*  @param function      e    the method to handle each callback with context
*/
Codagenic.A.prototype.each = function (e)
{
    var i = 0,
        l = this.length;

    for ( ; i < l; i++)
    {
        if (e.apply(this[i], [i]) === false)
        {
            break;
        }
    }

    return this;
};


/*
*  Find an object in array
*
*  @param function      func    the iterator
*
*  returns the found object or null if not found
*/
Codagenic.A.prototype.find = function (func)
{
    for (var i = 0, l = this.length; i < l; ++i)
    {
        var item = this[i];
        if (func(item))
            {return item;}
    }
    return null;
};

/*
*  object exists in array
*
*  @param object      sender    the object
*
*  returns boolean if found object
*/
Codagenic.A.prototype.has = function (sender)
{
	var r = this.find(function (item)
	{
		return item === sender;
	});

	if (r === null)
	{
		return false;
	}

	return true;
};


/*
*  Find object(s) in array
*
*  @param function      func    the iterator
*
*  returns an array of found objects
*/
Codagenic.A.prototype.where = function (func)
{
    var found = Codagenic.A();
    for (var i = 0, l = this.length; i < l; ++i)
    {
        var item = this[i];
        if (func(item))
            {found.push(item);}
    }
    return found;
};

/*
*  First object in array
*
*  returns the object
*/
Codagenic.A.prototype.first = function ()
{
    return this[0];
};

/*
*  Last object in array
*
*  returns the object
*/
Codagenic.A.prototype.last = function ()
{
    return this[this.length - 1];
};

/*
*  Convert to native array
*
*  returns native array
*/
Codagenic.A.prototype.toNative = function ()
{
    var items = [];
    for (var i = 0, l = this.length; i < l; ++i)
    {
        items.push(this[i]);
    }
    return items;
};

/*
*  Clone array
*
*  returns cloned array
*/
Codagenic.A.prototype.clone = function ()
{
    return Codagenic.A(this);
};

Codagenic.A.prototype.toString = function ()
{
	return this.join();
};
;/* =============================================================
* codagenicJS Library
* http://www.codagenic.com/
* =============================================================
* Copyright 2012 Codagenic, All Rights Reserved.
*
* Created by Byron Salau
* ============================================================ */


/**
*  Creates an Codagenic.Object from an Object like object.
*
*  @param   Object     obj     The native object (optional)
*
*  @returns Codagenic.Object
*/
Codagenic.O = function (obj)
{
    if (this instanceof Codagenic.O)
    {
        if (Codagenic.isObject(obj))
        {
            for (var p in obj)
            {
                if (obj.hasOwnProperty(p)) {this[p] = obj[p];}
            }
        }

        return this;
    }


    return new Codagenic.O(obj);

};

/*
*    Extend Native Object
*/
Codagenic.O.prototype = {};


/*
*    Broadcast class
*/
Codagenic.O.prototype.toString = function ()
{
    return CODAGENIC_O_CLASS;
};


/*
*  Itterates over the objects properties
*
*  @param function      e               the method to handle each callback with context
*  @param boolean       incPrototypes   Include the objects prototypes in the loop
*/
Codagenic.O.prototype.each = function (e)
{
    var key,
        i = 0;
    for (key in this)
    {
        if (this.hasOwnProperty(key))
        {
            if (e.apply(this[key], [i]) === false)
            {
                break;
            }
            i++;
        }
    }

    return this;
};


/*
*  Clone
*
*  returns the cloned object
*
*/
Codagenic.O.prototype.clone = function ()
{
    return Codagenic.copy(this);
};

/*
*  Test if Object is an Array
*
*  @returns bool     returns 'true' if object is Array   
*/
Codagenic.O.prototype.isArray = function ()
{
    return Codagenic.isArray(this);
};

/*
*  Test if Object is Function
*
*  @returns bool     returns 'true' if object is Function   
*/
Codagenic.O.prototype.isFunction = function ()
{
    return Codagenic.isFunction(this);
};

/*
*  Test if Object is Number
*
*  @returns bool     returns 'true' if object is Number   
*/
Codagenic.O.prototype.isNumber = function ()
{
    return Codagenic.isNumber(this);
};


/*
*  Test if Object is String
*
*  @returns bool     returns 'true' if object is String   
*/
Codagenic.O.prototype.isString = function ()
{
    return Codagenic.isString(this);
};

/*
*  Test if Object is Date
*
*  @returns bool     returns 'true' if object is Date   
*/
Codagenic.O.prototype.isDate = function ()
{
    return Codagenic.isDate(this);
};

/*
*  Test if Object is eqaul
*
*  @returns bool     returns 'true' if object is equal   
*/
Codagenic.O.prototype.equals = function (x)
{
    var p;
    for (p in this)
    {
        if (typeof (x[p]) === 'undefined') { return false; }
    }

    for (p in this)
    {
        if (this[p])
        {
            switch (typeof (this[p]))
            {
                case 'object':
                    if (!this[p].equals(x[p])) { return false; } break;
                case 'function':
                    if (typeof (x[p]) === 'undefined' ||
                        (p !== 'equals' && this[p].toString() !== x[p].toString()))
                        {return false;}
                    break;
                default:
                    if (this[p] !== x[p]) { return false; }
            }
        } else
        {
            if (x[p])
                {return false;}
        }
    }

    for (p in x)
    {
        if (typeof (this[p]) === 'undefined') { return false; }
    }

    return true;
};
;/* =============================================================
* codagenicJS Library
* http://www.codagenic.com/
* =============================================================
* Copyright 2012 Codagenic, All Rights Reserved.
*
* Created by Byron Salau
* ============================================================ */

/**
*  Encapsulate the native string inside an object
*
*  @param   Object     obj     The string to wrap
*
*  @returns String
*/
Codagenic.S = function (obj)
{
    var _string = new String(obj);

    _string.constructor = Codagenic.S;

    for (var i in Codagenic.S.prototype)
    {
        if (!Object.prototype[i])
        {
            _string[i] = Codagenic.S.prototype[i];
        }
    }

    return _string;
};

/*
*  Truncates a string to given `length` and appends `suffix` to it
*
*  @param Number      length    (optional)  the length of MAX characters to return
*  @param String      suffix    (optional)  the string to append indicate it has been truncated.
*
*  @returns String
*/
Codagenic.S.prototype.truncate = function (length, suffix)
{
    length = length || 30;
    suffix = Codagenic.isDefined(suffix) && Codagenic.isString(suffix) ? suffix : '...';
    return this.length > length ? this.slice(0, length - suffix.length) + suffix : String(this);
};


/*
*  Strips a string of any HTML tags.
*
*  Note that will only strip HTML 4.01 tags - like
*  `div`, `span`, and `abbr`. It will not strip namespace-prefixed tags
*  such as `h:table` or `xsl:template`.
*
*  @returns String          returns left, and retains right
*/
Codagenic.S.prototype.stripTags = function ()
{
    return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
};


/*
*  Email Check
*
*  @returns Boolean     true if the value is an email address
*/
Codagenic.S.prototype.isValidEmailAddress = function ()
{
    var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    return re.test(this);
};

/*
*  Value/Length Check
*
*  @returns Boolean     true if string is not empty
*/
Codagenic.S.prototype.isNullOrEmpty = function ()
{
    return this.length > 0;
};

/*
*  Starts With
*
*  @param   String      prefix     the string to test
*
*  @returns Boolean                returns true if string starts with prefix
*/
Codagenic.S.prototype.startsWith = function (prefix)
{
    return (this.indexOf(prefix) === 0);
};

/*
*  Ends With
*
*  @param   String      suffix     the string to test
*
*  @returns Boolean                 returns true if string ends with suffix
*/
Codagenic.S.prototype.endsWith = function (suffix)
{
    return this.indexOf(suffix, this.length - suffix.length) !== -1;
};




/*
*  Append String
*
*  @param   String      str     the string to append
*
*  @returns String              returns left, and retains right
*/
Codagenic.S.prototype.appendString = function (str)
{
    return Codagenic.S(this + str);
};

/*
*  Prepend String
*
*  @param   String      str     the string to prepend
*
*  @returns String              returns left, and retains right
*/
Codagenic.S.prototype.prependString = function (str)
{
    return Codagenic.S(str + this);
};


/*
*  Set URL Param
*  (eg ?key=value)
*
*  @param   String      key     the parameter key
*  @param   String      value   the parameter value
*
*  @returns String              returns left, and retains right
*/
Codagenic.S.prototype.setUrlParam = function (key, value)
{
    var re = new RegExp("([?|&])" + key + "=.*?(&|$)", "i"),
    separator = this.indexOf('?') !== -1 ? "&" : "?";
    if (this.match(re))
    {
        return Codagenic.S(re, '$1' + key + "=" + value + '$2');
    }
    else
    {
        return Codagenic.S(this + separator + key + "=" + value);
    }
};
;


// =====================================================
// Address Switch
// =====================================================
//
// REQUIRED: 
// USAGE:
// TODO:
Codagenic.addressSwitch = function (element, options) {

    // @private
    //
    // To avoid scope issues, we use 'base' instead of 'this'
    // to reference this class from internal events and functions.
    var base = this,
        $element = $(element),  // reference to the jQuery version of DOM element

        defaults =              // default options
        {

        };

    // the "constructor" method that gets called when the object is created
    base.init = function () {

        // the plugin's final properties are the merged default and
        // user-provided options (if any)
        base.options = $.extend({}, defaults, options);
        base.id = $element.data('id');


        $element.find('select').each(function (i) {

            var _options = base.options.defaultControl;



        });
    };

    // call the "constructor" method
    base.init();

};
;/* =============================================================
* codagenicJS Library
* http://www.codagenic.com/
* =============================================================
* Copyright 2012 Codagenic, All Rights Reserved.
*
* Created by Byron Salau
* ============================================================ */



/**
* BaseDataAccess
*/
var BaseDataAccess = {};


/**
*  Client Default properties
*/
var BaseDataAccessDefaults =
{
    baseURI: '/Store/Services/',
    productPath: 'Products.ashx',
    assetPath: 'Assets.ashx',
    ordersPath: 'Orders.ashx',
    wishlistPath: 'Wishlist.ashx',
    async: true,
    cache: true,
    pushHistory: false
};



/*
* MAIN FETCH METHOD
*/
BaseDataAccess._createRequest = function (request, success, failure, postData)
{
    if (!Codagenic.isString(request))
    {
        throw Error("Invalid Request");
    }

    // To avoid scope issues, we use 'base' instead of 'this'
    // to reference this class from internal events and functions.
    var self = this,
        options = null,
        requestType = 'GET';

    // find the request options
    if (Codagenic.isSet(self.settings))
    {
        options = self.settings;
    }
    else
    {
        options = BaseDataAccessDefaults;
    }
    
    if (Codagenic.isDefined(postData))
    {
        requestType = 'POST';
    }

    var successHandler = function (data)
    {
        // Successful return but lets determine the "status" from the service
        // and handle accordionly
        if (Codagenic.isDefined(data.status))
        {
            if (data.status === "success")
            {
                Codagenic.Callback(options.success, data.data);
                Codagenic.Callback(success, data.data);
            }
            else if (data.status === "failure")
            {
                Codagenic.Callback(options.failure, data.data);
                Codagenic.Callback(failure, data.data);
            }
        }
        else
        {
            Codagenic.Callback(options.failure, data);
            Codagenic.Callback(failure, data);
        }
    };

    var failureHandler = function (data)
    {
        Codagenic.Callback(options.failure, data);
        Codagenic.Callback(failure, data);
    };

    // Make the underlying ajax request
    $.ajax(
	{
		url: request,
		dataType: 'json',
		type: requestType,
		data: postData,
		async: options.async,
		cache: options.cache,
		success: successHandler,
		failure: failureHandler
	});

    return this;
};

;


Codagenic.ProductDataAccess = function (settings)
{
    if ((this instanceof Codagenic.ProductDataAccess) === false)
    {
        throw new Error("Static access of this class not allowed. You must create an instance of this class using the 'new' keyword.");
    }

    this.settings = $.extend({}, BaseDataAccessDefaults, settings);

    return this;
};

Codagenic.ProductDataAccess.prototype = $.extend({}, BaseDataAccess);

Codagenic.ProductDataAccess.prototype.loadProduct = function (productId, success, failure) 
{
    var request = this.settings.baseURI + this.settings.productPath + "?type=product&id=" + productId;

    this._createRequest(request, function (product)
    {
        Codagenic.Callback(success, new Codagenic.Product(product));
    }, function (data)
    {
        Codagenic.Callback(failure, data);
    });

    return this;
};

Codagenic.ProductDataAccess.prototype.addProductToCart = function (productId, variationId, qty, success, failure)
{
    var self = this,
        request = self.settings.baseURI + self.settings.ordersPath;

    this._createRequest(request, function (data)
    {
        Codagenic.Callback(success, data);
    }, function (data)
    {
        Codagenic.Callback(failure, data);
    },
    {
        type: 'add',
        id: productId,
        vid: variationId,
        quantity: qty
    });

    return this;
};

Codagenic.ProductDataAccess.prototype.addProductToWishlist = function (productId, variationId, qty, success, failure)
{
    var self = this,
        request = self.settings.baseURI + self.settings.wishlistPath;

    this._createRequest(request, function (data)
    {
        Codagenic.Callback(success, data);
    }, function (data)
    {
        Codagenic.Callback(failure, data);
    },
    {
        type: 'add',
        id: productId,
        vid: variationId,
        quantity: qty
    });

    return this;
}


Codagenic.ProductDataAccess.prototype.listProducts = function (categoryId, pageSize, pageNumber, success, failure)
{
	var self = this,
        request = self.settings.baseURI + self.settings.productPath + "?type=category&id=" + categoryId;

	if (Codagenic.isNumber(pageSize))
	{
		request = request + "&pageSize=" + pageSize;
	}

	if(Codagenic.isNumber(pageNumber))
	{
		request = request + "&pageNumber=" + pageNumber;
	}

	this._createRequest(request, function (data)
	{
		Codagenic.Callback(success, data);
	}, function (data)
	{
		Codagenic.Callback(failure, data);
	});

	return this;

};
;/*
*  api.product.js
*  CodagenicJS
*
*  Created by Byron Salau
*  Copyright 2012 Codagenic. All rights reserved.
*/


/*
*  PRODUCT MODEL
*/
Codagenic.Product = function (data)
{
	this.id = data.id;
	this.defaultAsset = data.defaultAsset;
	this.title = data.title;
	this.url = data.url;
	this.variations = Codagenic.A();
	this.video = data.video;
	this.images = Codagenic.A(data.images);
	this.videos = Codagenic.A(data.videos);
	this.documents = Codagenic.A(data.documents);

	for (var i = 0; i < data.variations.length; i++)
	{
		this.variations.push(new Codagenic.Variation(data.variations[i]));
	}

	return this;
};

/*
*  RETURNS: bool if the product has images
*/
Codagenic.Product.prototype.hasImages = function ()
{
	return Codagenic.isSet(this.images);
};

/*
*  RETURNS: bool if the product has documents
*/
Codagenic.Product.prototype.hasDocuments = function ()
{
	return Codagenic.isSet(this.documents);
};

/*
*  RETURNS: bool if the product has videos
*/
Codagenic.Product.prototype.hasVideos = function ()
{
	return Codagenic.isSet(this.videos);
};

/*
*  RETURNS: bool if the product has assets
*/
Codagenic.Product.prototype.hasAssets = function ()
{
	if(this.hasImages()){ return true; }
	if(this.hasDocuments()){ return true; }
	if(this.hasVideos()){ return true; }
	return false;
};

/*
*  RETURNS: an object of the product assets
*/
Codagenic.Product.prototype.getAssets = function ()
{
	var assets = Codagenic.O();
	assets.images = this.images;
	assets.documents = this.documents;
	assets.videos = this.videos;

	return assets;
};


/*
*  SUMMARY: Returns the products default variation
*/
Codagenic.Product.prototype.defaultVariation = function ()
{
	var v = null;

	this.variations.each(function ()
	{
		if (this.isDefault)
		{
			v = this;
			return false; // Breaks each loop
		}
	});

	if (!v && this.variations.length)
	{
		v = this.variations[0];
	}

	return v;
};

/*
*  PARAM: attributesToMatch *array - An Array of objects with title and value property pairs
*  RETURNS: Matched variation or null if not found
*/
Codagenic.Product.prototype.findVariationWithAttributes = function (attributesToMatch)
{
	if (!Codagenic.isDefined(attributesToMatch))
	{
		return null;
	}

	var v = null;

	this.variations.each(function ()
	{
		var matchCount = 0;

		// loop through attributes and check that we have a match
		for (var i = 0; i < attributesToMatch.length; i++)
		{
			var attributeToMatch = attributesToMatch[i];

			this.attributes.each(function ()
			{
				if (this.title === attributeToMatch.title && this.value === attributeToMatch.value)
				{
					matchCount++;
				}
			});
		}

		if (matchCount === this.attributes.length)
		{
			v = this; // this is our variation
			return false; // Breaks each loop
		}
	});

	return v;
};

/*
*  PARAM: title *string - the title of the attribute to match
*  PARAM: value *string - the value of the attribute to match
*  PARAM: hasStock *bool (optional) - if true only returns matches that have stock available
*  RETURNS: Variations that match a variation key value pair
*/

/*
*  Itterates all variations and finds all unique attributes
*
*  @param selectableOnly Boolean, if true only includes attributes that have selectable set to true 
*  @returns Codagenic.Array of the Attributes
*/
Codagenic.Product.prototype.listVariationsWithAttribute = function (title, value, hasStock)
{
	var list = Codagenic.A();
	this.variations.each(function ()
	{
		if (this.hasAttribute(title, value))
		{
			if (typeof hasStock === "boolean")
			{
				if (this.hasStock()) { list.push(this); }
			}
			else if (typeof hasStock === "undefined")
			{
				list.push(this);
			}
		}
	});
	return list;
};

/*
*  Itterates all variations and finds all unique attributes
*
*  @param selectableOnly Boolean, if true only includes attributes that have selectable set to true 
*  @returns Codagenic.Array of the Attributes
*/
Codagenic.Product.prototype.listUniqueAttributes = function (selectableOnly)
{
	var self = this,
		items = Codagenic.A();

	if (typeof selectableOnly !== 'boolean')
	{
		selectableOnly = false; //Default
	}

	self.variations.each(function ()
	{
		var variation = this;

		this.attributes.each(function ()
		{
			if ((!selectableOnly) || (selectableOnly && this.selectable))
			{
				var attribute =
					new Codagenic.Attribute({
						title: this.title,
						value: this.value,
						selectable: this.selectable,
						variation: variation
					}),

					hasAttribute = false;

				items.each(function ()
				{
					// look for and add if found
					if (this.title === attribute.title)
					{
						this.addAttribute(attribute);
						hasAttribute = true;
						return false;
					}
				});

				if (!hasAttribute)
				{
				    var collection = new Codagenic.AttributeCollection();
				    collection.title = attribute.title;
				    collection.addAttribute(attribute);
				    items.push(collection);
				}
			}
		});
	});

	return items;
};


/*
*  PARAM: attributesToMatch *array - An Array of objects with title and value property pairs
*  RETURNS: Matched variation or null if not found
*/
Codagenic.Product.prototype.listAssetsForVariation = function (v)
{
	var self = this,
		target = null;
	if (!Codagenic.isSet(v))
		{throw "Variation is not set or undefined";}

	if (!(v instanceof Codagenic.Variation))
		{throw v + " is not an instance of Codagenic.Variation";}

	if (Codagenic.isSet(self.images) ||
		Codagenic.isSet(self.documents) ||
		Codagenic.isSet(self.videos))
	{
		// Default get assets from the product level
		target = self;
	}

	if (Codagenic.isSet(v.images) ||
		Codagenic.isSet(v.documents) ||
		Codagenic.isSet(v.videos))
	{
		// If the varation contains assets, get from variation instead
		target = v;
	}

	if (target)
	{
		var assets = Codagenic.O();
		assets.images = Codagenic.A(target.images);
		assets.documents = Codagenic.A(target.documents);
		assets.videos = Codagenic.A(target.videos);
		return assets;
	}

	return null;

};



;

/*
*  ATTRIBUTE MODEL
*/
Codagenic.Attribute = function (obj) {

    this.title = obj.title;
    this.selectable = obj.selectable;
    this.value = obj.value;
    this.variation = obj.variation;

    return this;
};

;

/*
*  AttributeCollection MODEL
*/
Codagenic.AttributeCollection = function (obj) {

    // Public
    this.title = obj ? obj.title || null : null;
    this.values = obj ? Codagenic.A(obj.values) || Codagenic.A() : Codagenic.A();

    return this;
};



Codagenic.AttributeCollection.prototype.addAttribute = function (attribute) {
    for (var i = 0; i < this.values.length; i++) {
        if (this.values[i].value === attribute.value) {
            return; // Dont duplicate values
        }
    }

    this.values.push(attribute);
};


/*
*   Re-order values
* 
*   @param   Array     values     The Array of possible values in the desired order
*
*   returns the sorted AttributeCollection    (Unknonws appended to the front)
*/
Codagenic.AttributeCollection.prototype.sortByValues = function (values)
{
    if (!Codagenic.isArray(values) && !Codagenic.isSet(values))
    { throw "Unexpected Object"; }

    this.values.sort(function (a, b) {
        return (values.indexOf(a.value)) - (values.indexOf(b.value));
    });

    return this;
};
;

/*
*  VARIATIONS MODEL
*/
Codagenic.Variation = function (obj)
{
    // Variation with object
    if (typeof obj !== 'undefined') {
        this.id = obj.id;
        this.attributes = Codagenic.A(obj.attributes);
        this.code = obj.code;
        this.defaultPrice = Codagenic.O(obj.defaultPrice);
        this.currentPrice = Codagenic.O(obj.currentPrice);
        this.qty = obj.quantity;
        this.sku = obj.sku;
        this.swatchImage = obj.swatchImage;
        this.swatchCode = obj.swatchColourCode;
        this.values = Codagenic.A();
        this.isDefault = obj.defaultVariation;
        this.images = Codagenic.A(obj.images);
        this.videos = Codagenic.A(obj.videos);
        this.documents = Codagenic.A(obj.documents);
    }

    return this;
};



/*
*  RETURNS: bool if the variation has an attribute that matches a key value pair
*  PARAM: title *string - the title of the attribute to match
*  PARAM: value *string - the value of the attribute to match
*/
Codagenic.Variation.prototype.hasAttribute = function (title, value) {
    var result = false;
    this.attributes.each(function () {
        if (this.title === title && this.value === value) { result = true; return false; }
    });
    return result;
};

/*
*  RETURNS: bool if the variation has stock available
*/
Codagenic.Variation.prototype.hasStock = function () {
    return this.qty > 0;
};

/*
*  RETURNS: bool if the variation has images
*/
Codagenic.Variation.prototype.hasImages = function () {
    return Codagenic.isSet(this.images);
};

/*
*  RETURNS: bool if the variation has documents
*/
Codagenic.Variation.prototype.hasDocuments = function () {
    return Codagenic.isSet(this.documents);
};

/*
*  RETURNS: bool if the variation has videos
*/
Codagenic.Variation.prototype.hasVideos = function () {
    return Codagenic.isSet(this.videos);
};

/*
*  RETURNS: bool if the variation has assets
*/
Codagenic.Variation.prototype.hasAssets = function () {
    if (this.hasImages()) { return true; }
    if (this.hasDocuments()) { return true; }
    if (this.hasVideos()) { return true; }
    return false;
};

/*
*  RETURNS: an object of the variation assets
*/
Codagenic.Variation.prototype.getAssets = function () {
    var assets = Codagenic.O();
    assets.images = this.images;
    assets.documents = this.documents;
    assets.videos = this.videos;

    return assets;
};

;

    // =====================================================
    // TOGGLE PANEL
    // =====================================================
    //
    // REQUIRED: jQuery
    // USAGE:
    // TODO:
Codagenic.togglePanel = function (element, options)
{

    // @private
    //
    // To avoid scope issues, we use 'base' instead of 'this'
    // to reference this class from internal events and functions.
    var base = this,
            defaults =              // default options
            {
                panel: null,        // Target jquery element to show/hide
                toggleBtn: null,
                showOnInit: false,
                showBtn: null,
                hideBtn: null,
                animated: false,
                duration: 400,
                easing: "linear",

                onShow: F,
                onHide: F
            };

    // the "constructor" method that gets called when the object is created
    base.init = function ()
    {

        // If element passed in from jquery fn then set it as 
        // default before we extend options
        if (element)
        {
            defaults.panel = $(element);
        }

        // the plugin's final properties are the merged default and
        // user-provided options (if any)
        base.options = $.extend({}, defaults, options);

        // Dont do anything if no panel to show/hide or
        // Dont do anything if panel isnt a jquery object
        if (!base.options.panel || !Codagenic.isJquery(base.options.panel))
        {
            return;
        }

        // flag, used to detect if panel is on or off for toggle events
        var _open = false;

        // Detect if the panel is actually visible at the moment
        if (base.options.panel.is(':visible'))
        {
            // Panel is on/true/visible
            _open = true;
        }

        // Logic that shows the panel
        base.showPanel = function ()
        {
            if (base.options.animated)
            {
                base.options.panel.slideDown(base.options.duration, base.options.easing, function ()
                {
                    _open = true;
                    base.options.onShow(); // Callback
                });
            }
            else
            {
                _open = true;
                base.options.panel.show();
                base.options.onShow(); // Callback
            }
        };

        // logic that hides the panel
        base.hidePanel = function ()
        {
            if (base.options.animated)
            {
                base.options.panel.slideUp(base.options.duration, base.options.easing, function ()
                {
                    _open = false;
                    base.options.onHide();
                });
            }
            else
            {
                _open = false;
                base.options.panel.hide();
                base.options.onHide(); // Callback
            }
        };

        // Show button event handler
        if (Codagenic.isJquery(base.options.showBtn))
        {
            base.options.showBtn.on("click", function (e)
            {
                base.showPanel();
                e.preventDefault();
            });
        }

        // Hide button event handler
        if (Codagenic.isJquery(base.options.hideBtn))
        {
            base.options.hideBtn.on("click", function (e)
            {
                base.hidePanel();
                e.preventDefault();
            });
        }

        // Toggle button event handler
        if (Codagenic.isJquery(base.options.toggleBtn))
        {
            base.options.toggleBtn.on("click", function (e)
            {
                if (_open)
                {
                    base.hidePanel();
                }
                else
                {
                    base.showPanel();
                }

                e.preventDefault();
            });
        }

        // Panel Default
        if (base.options.showOnInit)
        {
            base.showPanel();
        }
        else
        {
            base.hidePanel();
        }
    };

    // call the "constructor" method
    base.init();

};

    //jQuery effin
    $.fn.togglePanel = function(options){
        return this.each(function () {
          if (!$.data(this, 'codagenic_togglePanel'))
          {
            $.data(this, 'codagenic_togglePanel', new Codagenic.togglePanel( this, options ));
          }
        });
    };
; 
	// =====================================================
	// Controls Class
	// =====================================================
	//
	// REQUIRED: UI
	// USAGE:
	// TODO:
	Codagenic.addToCart = function(element, options) {

		// @private
		//
		// To avoid scope issues, we use 'base' instead of 'this'
		// to reference this class from internal events and functions.
		var base = this,
			$element = $(element),  // reference to the jQuery version of DOM element

			defaults =              // default options
			{
				controls: {},
				defaultControl:
				{
					type: "dropdown",
					cssClass: "codagenic-control",
					itemTemplate: function( item ){ return item.title; },
					prepend: "",
					append: "",

					//callbacks
					onSelect: function(){}
				}
			};

		// constructor method
		base.init = function() {

			// the plugin's final properties are the merged default and
			// user-provided options (if any)
			base.options =  $.extend({}, defaults, options);
			base.id =       $element.data('id');


			$element.find('select').each(function(i){

				var _options = base.options.defaultControl,
					key = $(this).attr("title");

				// Check if developer has specified any custom settings.
				if(typeof base.options.controls[key] !== "undefined" && key !== "")
				{
					//developer has specific options
					_options = $.extend({}, base.options.defaultControl, base.options.controls[key]);
				}

				$(this).codagenic("setUI", {selectBox: _options} );

			});
		};

		// call the "constructor" method
		base.init();

	};
;  
  
	// phew!
	// Create a global reference to our library.
	global.Codagenic = global.Cg = Codagenic;

/*jsl:ignore*/
})(this, jQuery, window);
/*jsl:end*/